#include <stdio.h>
#include "tcl_wrapper.h"
#include "baseDB.h"

extern vector< vector<int> > TDM_LOC;
extern unsigned int XDIM,YDIM,ZDIM;
extern unsigned int XFREQ,YFREQ,TDMDIST;

int base_init_placement_env (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {

    if (argc != 4)
        return TCL_ERROR;
    int x,y,z;

    if (Tcl_GetInt(interp,argv[1],&x) != TCL_OK) {
        printf("Error: can not get x %s\n",argv[1]);
        return TCL_ERROR;
    }

    if (Tcl_GetInt(interp,argv[2],&y) != TCL_OK) {
        printf("Error: can not get y %s\n",argv[2]);
        return TCL_ERROR;
    }

    if (Tcl_GetInt(interp,argv[3],&z) != TCL_OK) {
        printf("Error: can not get z %s\n",argv[3]);
        return TCL_ERROR;
    }
    init_3d_grid((unsigned int)x,(unsigned int)y,(unsigned int)z);
    return TCL_OK;
}

int base_init_tdm_env (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {

    if (XDIM==0||YDIM==0||ZDIM==0) {
        printf("Error: bs_init_place_env must run before setting tdm\n");
        return TCL_ERROR;
    }

    if (argc != 4)
        return TCL_ERROR;

    int xfreq,yfreq,tdmdist;
    if (Tcl_GetInt(interp,argv[1],&xfreq) != TCL_OK) {
        printf("Error: can not get xfreq %s\n",argv[1]);
        return TCL_ERROR;
    }

    if (Tcl_GetInt(interp,argv[2],&yfreq) != TCL_OK) {
        printf("Error: can not get yfreq %s\n",argv[2]);
        return TCL_ERROR;
    }

    if (Tcl_GetInt(interp,argv[3],&tdmdist) != TCL_OK) {
        printf("Error: can not get tdmdist %s\n",argv[2]);
        return TCL_ERROR;
    }

    XFREQ = (unsigned int)xfreq;
    YFREQ = (unsigned int)yfreq;
    TDMDIST = (unsigned int)tdmdist;

    unsigned int xtdm = XDIM/XFREQ;
    unsigned int ytdm = YDIM/YFREQ;
    unsigned int idx = 0;

    TDM_LOC.resize(xtdm*ytdm);

    for (unsigned int i=0;i<xtdm;i++) {
        for (unsigned int j=0;j<ytdm;j++) {
            TDM_LOC[idx].push_back((i+1)*XFREQ);
            TDM_LOC[idx].push_back((j+1)*YFREQ);
            idx++;
        }
    }

    return TCL_OK;
}

// base_AddCell instname typename
int base_AddCell (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {
    
    string name;
    string type;
    
    if (argc != 3)
        return TCL_ERROR;

    name.assign(argv[1]);
    type.assign(argv[2]);

    if (type.compare("IOIN") == 0)
        add_cell(name.data(),e_ioin);
    else if (type.compare("IOOUT") == 0)
        add_cell(name.data(),e_ioout);
    else if (type.compare("CLB") == 0)
        add_cell(name.data(),e_clb);
    else {
        printf("Error: can not identify cell type %s\n",type.data());
        return TCL_ERROR;
    }

    return TCL_OK;
}

// base_AddEdge driver_instname load_instname
int base_AddEdge (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {
    
    C_CELL* pDriver = NULL;
    C_CELL* pLoad = NULL;
    unsigned int DriverIdx,LoadIdx;
    
    if (argc!=3) {
        printf("Usage: BASE_ADDFANOUT instname instname\n");
        return TCL_ERROR;
    }

    pDriver = get_cell_ptr(argv[1]);
    DriverIdx = get_cell_idx(argv[1]);
    pLoad = get_cell_ptr(argv[2]);
    LoadIdx = get_cell_idx(argv[2]);

    if ((pDriver != NULL) && (pLoad != NULL)) {
        pDriver->addFanout(LoadIdx);
        pLoad->addFanin(DriverIdx);
        return TCL_OK;
    }

    if (pDriver == NULL)
        printf("Error: cannot find %s\n",argv[1]);
    if (pLoad == NULL)
        printf("Error: cannot find %s\n",argv[2]);

    return TCL_ERROR;
}

//
int base_SetCellLoc (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {
    
    if (argc != 5)
        return TCL_ERROR;
    int x,y,z;

    if (Tcl_GetInt(interp,argv[2],&x) != TCL_OK) {
        printf("Error: can not get x %s\n",argv[2]);
        return TCL_ERROR;
    }

    if (Tcl_GetInt(interp,argv[3],&y) != TCL_OK) {
        printf("Error: can not get y %s\n",argv[3]);
        return TCL_ERROR;
    }

    if (Tcl_GetInt(interp,argv[4],&z) != TCL_OK) {
        printf("Error: can not get z %s\n",argv[4]);
        return TCL_ERROR;
    }

    if (set_cell_loc(argv[1],x,y,z))
        return TCL_OK;
    else
        return TCL_ERROR;
}

int base_ReportStat (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {
    
    float WL;
    int interlayerbandwidth;
    
    placement_statistics(WL,interlayerbandwidth);
    printf ("WireLength: %f\n",WL);
    printf ("Interlayer Width: %d\n",interlayerbandwidth);
    return TCL_OK;
}

int base_printNetwork (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {
    
    print_network();
    return TCL_OK;
}

int base_printcell (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {
    
    C_CELL* pCell = NULL;
    if (argc!=2) {
        printf("Usage: bs_print_cell instname\n");
        return TCL_ERROR;
    }
    pCell = get_cell_ptr(argv[1]);
    
    if (pCell!=NULL) {
        int x,y,z;
        printf("Cell Name: %s\n",pCell->getName());
        pCell->getLoc(x,y,z);
        printf("    Loc (x,y,z)=(%d,%d,%d)\n",x,y,z);
        printf("    isFF = %d\n",pCell->isFF());
    } else
        return TCL_ERROR;

    return TCL_OK;
}

int base_randomly_place (ClientData clientData,
                Tcl_Interp *interp,
                int argc, const char *argv[]) {

    place_cell_randomly();
    return TCL_OK;
}
